﻿using System;
using System.Collections.Specialized;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;

namespace ItemsViewer
{
    /// <summary>
    /// This control allow to display items with animation.
    /// </summary>  
    public class ItemsViewerControl : ListBox
    {
        /// <summary>
        /// The animated virtualizing wrapPanel. 
        /// </summary>
        private AnimatedVirtualizingWrapPanel animatedVirtualizingWrapPanel;

        /// <summary>
        /// The filterAnimatedListCollectionView.
        /// </summary>
        private FilterAnimatedListCollectionView filterAnimatedListCollectionView;

        /// <summary>
        /// Initializes static members of the ItemsViewerControl class.
        /// </summary>
        static ItemsViewerControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ItemsViewerControl), new FrameworkPropertyMetadata(typeof(ItemsViewerControl)));
        }

        /// <summary>
        /// On Initialized.
        /// </summary>
        /// <param name="e">The EventArgs parameter.</param>
        protected override void OnInitialized(EventArgs e)
        {
            base.OnInitialized(e);
            this.Loaded += this.PivotViewer_Loaded;
        }

        /// <summary>
        /// On items Source changed, get newValue.
        /// </summary>
        /// <param name="oldValue">The oldValue.</param>
        /// <param name="newValue">The newValue.</param>
        protected override void OnItemsSourceChanged(System.Collections.IEnumerable oldValue, System.Collections.IEnumerable newValue)
        {
            if ((newValue != null) && (newValue is FilterAnimatedListCollectionView))
            {
                this.filterAnimatedListCollectionView = newValue as FilterAnimatedListCollectionView;
                this.filterAnimatedListCollectionView.FilterAnimatedUpdated += this.ItemsViewerControl_FilterAnimatedUpdated;
            }

            base.OnItemsSourceChanged(oldValue, newValue);
        }

        /// <summary>
        /// OnSelection changed scroll to Item.
        /// </summary>
        /// <param name="e">The SelectionChangedEventArgs.</param>
        protected override void OnSelectionChanged(SelectionChangedEventArgs e)
        {
            if (e.AddedItems.Count > 0)
            {
                if (this.animatedVirtualizingWrapPanel != null)
                {
                    this.animatedVirtualizingWrapPanel.ScrollToItem(e.AddedItems[0]);
                }
            }

            base.OnSelectionChanged(e);
        }

        /// <summary>
        /// On Items changed, remove disconnected item.
        /// </summary>
        /// <param name="e">The NotifyCollectionChangedEventArgs parameter.</param>
        protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                if (e.OldItems.Count > 0)
                {
                    this.animatedVirtualizingWrapPanel.RemoveDisconnectedItem();
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Reset)
            {
                this.animatedVirtualizingWrapPanel.CanAnimate = false;
            }

            base.OnItemsChanged(e);
        }

        /// <summary>
        /// On PivotViewer loaded, get animatedVirtualizingWrapPanel associated.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The RoutedEventArgs parameter.</param>
        private void PivotViewer_Loaded(object sender, RoutedEventArgs e)
        {
            var itemsPresenter = Template.FindName("itemsPresenter", this) as FrameworkElement;
            if (itemsPresenter != null)
            {
                var itemsPanel = ItemsPanel.FindName("itemsPanel", itemsPresenter);
                this.animatedVirtualizingWrapPanel = itemsPanel as AnimatedVirtualizingWrapPanel;
            }
        }

        /// <summary>
        /// On Filter updated, animate item.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The EventArgs.</param>
        private void ItemsViewerControl_FilterAnimatedUpdated(object sender, EventArgs e)
        {
            if (this.filterAnimatedListCollectionView.FilterAnimated != null)
            {
                foreach (object obj in this.Items)
                {
                    ListBoxItem item = this.ItemContainerGenerator.ContainerFromItem(obj) as ListBoxItem;
                    if (item == null)
                    {
                        continue;
                    }

                    // Invoke returns true if the data object should be displayed.
                    bool isFilteredIn = this.filterAnimatedListCollectionView.FilterAnimated.Invoke(obj);

                    if (isFilteredIn)
                    {
                        item.Visibility = Visibility.Visible;
                    }
                    else
                    {
                        item.Visibility = Visibility.Collapsed;
                    }
                }

                Task task = Task.Factory.StartNew(() =>
                {
                    Thread.Sleep(this.animatedVirtualizingWrapPanel.AnimationLength);
                    this.Dispatcher.BeginInvoke((Action)(() =>
                    {
                        filterAnimatedListCollectionView.Filter = filterAnimatedListCollectionView.FilterAnimated;
                        OnItemsChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
                        this.filterAnimatedListCollectionView.RaiseFilterAnimatedCompleted();
                    }), DispatcherPriority.Background);
                });
            }
        }
    }
}
